/* Copyright 2006 Nicklas R Johnson
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */

package net.spatula.tally_ho.service;

import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Query;

import net.spatula.tally_ho.jpa.ResourceManager;
import net.spatula.tally_ho.model.Account;
import net.spatula.tally_ho.model.Article;
import net.spatula.tally_ho.model.ArticleMessageBoardRoot;
import net.spatula.tally_ho.model.Section;
import net.spatula.tally_ho.service.beans.ArticleBean;
import net.spatula.tally_ho.service.beans.UserBean;
import net.spatula.tally_ho.utils.TextUtils;

/**
 * @author spatula
 * 
 */
public class ArticleService {

    private static ArticleService instance = null;

    private ArticleService() {

    }

    public static synchronized ArticleService getInstance() {
        if (instance == null) {
            instance = new ArticleService();
        }
        return instance;
    }

    public ServiceResult createArticle(ArticleBean articleBean) {
        EntityManagerFactory emf = ResourceManager.getInstance().getEMF();
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();

        Article article = new Article();
        copyIn(em, articleBean, article);
        Account creator = UserService.getInstance().getAccount(em, articleBean.getCreator());
        article.setCreator(creator);
        article.setChanger(creator);
        article.setChangeSummary("Initial article creation");

        em.persist(article);
        em.getTransaction().commit();
        articleBean.setId(article.getObjectId());
        articleBean.setCreateDate(TextUtils.formatDate(new Date(article.getCreateDate().getTime())));

        return new ServiceResult(ServiceResult.STATUS.OK, "Article was created successfully.  Article ID is "
                + article.getObjectId());
    }

    private void copyIn(EntityManager em, ArticleBean articleBean, Article article) {
        article.setAdjective(articleBean.getAdjective());
        article.setBody(articleBean.getBody());
        article.setCreateDate(new Timestamp(System.currentTimeMillis()));
        Section section = SectionService.getInstance().getSectionById(em, articleBean.getSection().getId());
        article.setSection(section);
        article.setStatus("U");
        article.setSummary(articleBean.getSummary());
        article.setTitle(articleBean.getHeadline());
        article.setVisible(true);

    }

    public void destroyArticle(EntityManager em, ArticleBean articleBean) {

        Account system = UserService.getInstance().getAccountByUsername(em, "System");
        Article destroyArticle = getArticleById(em, articleBean.getId());

        destroyArticle.setChanger(system);
        destroyArticle.setChangeSummary("System delete.");

        em.remove(destroyArticle);

    }

    private Article getArticleById(EntityManager em, long id) {
        return em.find(Article.class, id);
    }

    public ArticleBean getArticleById(long id) {
        EntityManagerFactory emf = ResourceManager.getInstance().getEMF();
        EntityManager em = emf.createEntityManager();

        ArticleBean articleBean = new ArticleBean();

        Article article = null;

        try {
            article = getArticleById(em, id);
        } catch (NoResultException e) {
            return null;
        }

        if (article == null) {
            return null;
        }

        articleBean.setAdjective(article.getAdjective());
        articleBean.setBody(article.getBody());
        articleBean.setCreateDate(TextUtils.formatDate(new Date(article.getCreateDate().getTime())));
        articleBean.setCreator(UserService.getInstance()
                .getBeanFromAccount(article.getCreator(), UserBean.Authenticator.NONE));
        articleBean.setHeadline(article.getTitle());
        articleBean.setId(article.getObjectId());
        ArticleMessageBoardRoot root = article.getMessageBoardRoot();
        if (root != null) {
            articleBean.setPostCount(root.getPostCount());
        }
        articleBean.setSection(SectionService.getInstance().getSectionBeanFromSection(article.getSection()));
        articleBean.setSummary(article.getSummary());
        articleBean.setThumbsDn(article.getThumbsDown());
        articleBean.setThumbsUp(article.getThumbsUp());
        articleBean.setSpamAbuse(article.getSpamAbuse());

        return articleBean;
    }

    public ServiceResult voteForArticle(ArticleBean articleBean, UserBean user) {
        return recordVote(articleBean, user, "thumbsUp");
    }

    private ServiceResult recordVote(ArticleBean articleBean, UserBean user, String field) {

        EntityManager em = ResourceManager.getInstance().getEM();

        long articleId = articleBean.getId();
        if (articleId < 1) {
            return new ServiceResult(ServiceResult.STATUS.FAIL_HARD, "Invalid or missing article ID");
        }

        Article article = null;
        try {
            article = getArticleById(em, articleId);
        } catch (NoResultException e) {
            return new ServiceResult(ServiceResult.STATUS.FAIL_HARD, "Article not found");
        }

        if (!user.isAuthenticated()) {
            return new ServiceResult(ServiceResult.STATUS.FAIL_HARD, "User not authenticated");
        }

        Account account = null;
        try {
            account = UserService.getInstance().getAccount(em, user);
        } catch (NoResultException e) {
            return new ServiceResult(ServiceResult.STATUS.FAIL_HARD, "User not found");
        }

        if (article.getArticleVotes().contains(account)) {
            return new ServiceResult(ServiceResult.STATUS.FAIL_HARD, user.getUsername()
                    + " has already voted on this article!");
        }

        try {
            em.getTransaction().begin();
            article.getArticleVotes().add(account);
            String getter = TextUtils.makeGetterName(field);
            String setter = TextUtils.makeSetterName(field);
            Method getMethod = article.getClass().getMethod(getter, (Class[]) null);
            Method setMethod = article.getClass().getMethod(setter, new Class[] { long.class });
            long currentCount = ((Long) getMethod.invoke(article, (Object[]) null)).longValue();
            setMethod.invoke(article, new Object[] { currentCount + 1 });

            em.getTransaction().commit();
        } catch (Exception e) {
            return new ServiceResult(ServiceResult.STATUS.FAIL_SOFT, "Error recording vote", e);
        }

        return new ServiceResult(ServiceResult.STATUS.OK, "Vote recorded successfully");

    }

    public ServiceResult voteAgainstArticle(ArticleBean articleBean, UserBean user) {
        return recordVote(articleBean, user, "thumbsDown");
    }

    public ServiceResult flagAsSpamAbuse(ArticleBean articleBean, UserBean user) {
        return recordVote(articleBean, user, "spamAbuse");
    }

    public List<String> getActions(ArticleBean article, UserBean user) {
        if (user == null || user.getAuthenticator() == UserBean.Authenticator.NONE) {

            return new ArrayList<String>(0);
        }
        EntityManager em = ResourceManager.getInstance().getEM();
        /*
         * if (!UserService.getInstance().verifyCredential(em, user)) { return new ArrayList<String>(0); } Account creator =
         * getArticleById(em, result.getId()).getCreator();
         */
        List<String> actions = new ArrayList<String>();
        Account account = UserService.getInstance().getAccount(em, user);
        if (hasWritePermission(em, article, account)) {
            actions.add("Update");
            actions.add("Delete");
        }

        return actions;
    }

    private boolean hasWritePermission(EntityManager em, ArticleBean articleBean, Account account) {
        UserBean creator = articleBean.getCreator();

        if (account.isAdmin() || account.isEditor() || account.isOwner()
                || account.getUsername().equals(creator.getUsername())) {
            return true;
        }
        return false;

    }

    public ServiceResult updateArticle(ArticleBean articleBean, UserBean userBean) {
        EntityManager em = ResourceManager.getInstance().getEM();
        Article article = getArticleById(em, articleBean.getId());

        if (article == null) {
            return new ServiceResult(ServiceResult.STATUS.FAIL_HARD, "Article not found.");
        }

        Account changer = UserService.getInstance().getAccount(em, userBean);
        
        if (changer == null) {
            return new ServiceResult(ServiceResult.STATUS.FAIL_HARD, "Account not found.");
        }
        
        if (!hasWritePermission(em, articleBean, changer)) {
            return new ServiceResult(ServiceResult.STATUS.FAIL_HARD, userBean.getUsername()
                    + " does not have permission to update this article.");
        }

        em.getTransaction().begin();
        copyIn(em, articleBean, article);
        
        article.setChanger(changer);
        article.setChangeSummary("Service update.");

        em.getTransaction().commit();
        return new ServiceResult(ServiceResult.STATUS.OK, "Article updated.");
    }

}
